﻿using System;
using FNN.Networks;
using System.Collections.Generic;
using FNN.SourcePoints;
using FNN.EventArguments;

namespace FNN.NetworkMasters
{
    public abstract class NetworkMaster : INetworkMaster
    {
        internal protected IEnumerable<SourcePoint> DataSourceValue;
        
        protected readonly Random Randomizer = new Random();

        protected int TimeLimitValue;
        protected int IterationsLimitValue;
        protected double TargetResultValue = -1.0;

        protected int PopulationCountValue;
        protected int GeneticTimeLimitValue;
        protected int GeneticTargetResultValue;
        protected int GeneticIterationsLimitValue;

        /// <summary>
        /// Желаемый результат культивированной сети
        /// </summary>
        public int GeneticTargetResult
        {
            get { return GeneticTargetResultValue; }
            set { GeneticTargetResultValue = value; }
        }

        /// <summary>
        /// Желаемый результат обученной сети
        /// </summary>
        public double TargetResult
        {
            get { return TargetResultValue; }
            set { TargetResultValue = value; }
        }

        /// <summary>
        /// Событие смены итерации
        /// </summary>
        public virtual event EventHandler<IterationChangedEventArgs> IterationChanged;
        /// <summary>
        /// Событие смены итерации с улучшением результатов
        /// </summary>
        public virtual event EventHandler<IterationChangedEventArgs> IterationResultsImproved;
        /// <summary>
        /// Событие смены поколения
        /// </summary>
        public virtual event EventHandler<IterationChangedEventArgs> GenerationChanged;
        /// <summary>
        /// Событие смены поколения с улучшением результатов
        /// </summary>
        public virtual event EventHandler<IterationChangedEventArgs> GenerationResultsImproved;

        /// <summary>
        /// Глобальное ограничение по времени при культивации сети
        /// </summary>
        public int GeneticTimeLimit
        {
            get { return GeneticTimeLimitValue; }
            set { GeneticTimeLimitValue = value; }
        }

        /// <summary>
        /// Глобальное ограничение по количеству эпох при культивации сети
        /// </summary>
        public int GeneticIterationsLimit
        {
            get { return GeneticIterationsLimitValue; }
            set { GeneticIterationsLimitValue = value; }
        }

        /// <summary>
        /// Ограничение по времени
        /// </summary>
        public int TimeLimit
        {
            get { return TimeLimitValue; }
            set { TimeLimitValue = value; }
        }

        /// <summary>
        /// Ограничение по эпохам
        /// </summary>
        public int IterationsLimit
        {
            get { return IterationsLimitValue; }
            set { IterationsLimitValue = value; }
        }

        /// <summary>
        /// Численность популяции
        /// </summary>
        public int PopulationCount
        {
            get { return PopulationCountValue; }
            set { PopulationCountValue = value; }
        }

        /// <summary>
        /// Исходные данные
        /// </summary>
        public IEnumerable<SourcePoint> DataSource
        {
            get { return DataSourceValue; }
            set { DataSourceValue = value; }
        }

        /// <summary>
        /// Проход эпохи обучения для сети
        /// </summary>
        /// <param name="target">Целевая сеть</param>
        /// <param name="isMultiNetwork">Множественность выходов сети</param>
        public abstract void RunEpoch(ref Network target, bool isMultiNetwork);

        /// <summary>
        /// Обучить сеть
        /// </summary>
        /// <param name="target">Целевая сеть</param>
        /// <returns></returns>
        public abstract Network TrainNetwork(Network target);

        /// <summary>
        /// Обучить сеть с множественными выходами
        /// </summary>
        /// <param name="target">Целевая сеть</param>
        /// <returns></returns>
        public abstract Network TrainMultiNetwork(Network target);

        /// <summary>
        /// Культивирование сети с использованием генетического алгоритма (изменение топологии)
        /// </summary>
        /// <param name="progenitor">Предок</param>
        /// <returns></returns>
        public abstract Network NetworkCultivation(Network progenitor, bool isMultiNetwork);

        /// <summary>
        /// Культивирование сети с использованием генетического алгоритма (подбор весов)
        /// </summary>
        /// <param name="progenitor">Предок</param>
        /// <param name="isMultiNetwork">Множественность выходов сети</param>
        /// <returns></returns>
        public abstract Network GeneticTrainingEpoch(Network progenitor, bool isMultiNetwork);

        /// <summary>
        /// Проход эпохи культивирования сети с нужной топологией
        /// </summary>
        /// <param name="targetGeneration">Текущее поколение</param>
        /// <param name="isMultiNetwork">Множественность выходов сети</param>
        /// <returns></returns>
        public abstract IList<Network> NetworkCultivationEpoch(IList<Network> targetGeneration, bool isMultiNetwork);

        /// <summary>
        /// Обучение сети с помощью генетического алгоритма
        /// </summary>
        /// <param name="target">Предок</param>
        /// <param name="isMultiNetwork">Множественность выходов сети</param>
        /// <returns>Обученная сеть</returns>
        public abstract Network GeneticNetworkTraining(Network target, bool isMultiNetwork);

        /// <summary>
        /// Генерация поколения на основе предка
        /// </summary>
        /// <param name="progenitor">Предок</param>
        /// <param name="neededPopulationCount">Необходимая численность</param>
        /// <returns>Новое поколение</returns>
        public abstract IList<Network> CreateGeneration(Network progenitor, int neededPopulationCount);

        /// <summary>
        /// Генерация поколения на основе предыдущего
        /// </summary>
        /// <param name="progenitorGeneration">Предыдущее поколение</param>
        /// <param name="populationMultiplier">Коэффициент увеличения популяции</param>
        /// <returns>Искомое поколение</returns>
        public abstract IList<Network> CreateGeneration(IEnumerable<Network> progenitorGeneration, int populationMultiplier);
    }
}